package com.xiaoshuidi.cloud.module.rooms.job.xxljob;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.framework.tenant.core.job.TenantJob;
import com.xiaoshuidi.cloud.module.rooms.controller.admin.roomel.vo.RoomelUpdateReqVO;
import com.xiaoshuidi.cloud.module.rooms.enums.*;
import com.xiaoshuidi.cloud.module.rooms.job.delayjob.DelayJobService;
import com.xiaoshuidi.cloud.module.rooms.mq.producer.ReserveProducer;
import com.xiaoshuidi.cloud.module.rooms.mq.producer.RoomEsProducer;
import com.xiaoshuidi.cloud.module.rooms.pojo.RoomHouse;
import com.xiaoshuidi.cloud.module.rooms.pojo.RoomHouseReserve;
import com.xiaoshuidi.cloud.module.rooms.pojo.RoomStateRecord;
import com.xiaoshuidi.cloud.module.rooms.repository.RoomHouseRepository;
import com.xiaoshuidi.cloud.module.rooms.repository.RoomHouseReserveRepository;
import com.xiaoshuidi.cloud.module.rooms.repository.RoomStateRecordRepository;
import com.xiaoshuidi.cloud.module.rooms.service.IRoomHouseReserveService;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.module.rooms.enums.DelayJobType.DelayJobTypeEnum.DEPOSIT_PAYMENT_TERM;
import static com.xiaoshuidi.cloud.module.rooms.enums.DelayJobType.DelayJobTypeEnum.EXPIRATION_DATE;

/**
 * @Author Feng
 * @date 2023/5/22
 */
@Slf4j
@TenantJob
@Component
@RequiredArgsConstructor
public class ReserveCancelJob {

    private final RoomHouseReserveRepository reserveRepository;
    private final RoomHouseRepository roomHouseRepository;
    private final RoomEsProducer roomEsProducer;
    private final RoomStateRecordRepository roomStateRecordRepository;
    private final ReserveProducer reserveProducer;
    private final DelayJobService delayJobService;
    private final IRoomHouseReserveService reserveService;

    private static String toBePayCancelReason = "未支付到期取消";
    private static String reservedCancelReason = "已支付到期取消";

    /**
     * 待支付取消
     */
    @XxlJob("room-reserve-toBePay-cancel-job")
    @Transactional(rollbackFor = Exception.class)
    public void toBePayCancel() {
        log.info("待支付预定单取消 - 执行开始");
        LocalDate now = LocalDate.now();
        LocalDateTime nowTime = LocalDateTime.now();
        //查询所有待支付且支付期限到当天截止的预定单,做取消操作
        List<RoomHouseReserve> list = reserveRepository.toBePayList(now);
        List<RoomHouse> roomHouseList = new ArrayList<>();
        List<RoomelUpdateReqVO> roomEsVoList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(list)) {
            list.forEach(s -> {
                s.setStatus(RoomHouseReserveStatus.CANCELED.getCode());
                s.setCancelTime(nowTime);
                s.setCancelReason(toBePayCancelReason);
                s.setPaymentStatus(RoomHouseReservePaymentStatus.CANCELED.getCode());
                RoomHouse roomHouse = new RoomHouse();
                roomHouse.setId(s.getRoomHouseId());
                roomHouse.setIsLock(Boolean.FALSE);
                roomHouseList.add(roomHouse);
                RoomelUpdateReqVO roomelUpdateReqVO = new RoomelUpdateReqVO();
                roomelUpdateReqVO.setIsLock(Boolean.FALSE);
                roomelUpdateReqVO.setId(s.getRoomHouseId());
                roomEsVoList.add(roomelUpdateReqVO);
            });
            reserveRepository.updateBatchById(list, list.size());
            roomHouseRepository.updateBatchById(roomHouseList, roomHouseList.size());
            roomEsProducer.updateBatchRoomEl(roomEsVoList);
        }
        log.info("待支付预定单取消 - 执行结束");
    }

    /**
     * 已预定取消
     */
    @XxlJob("room-reserve-reserved-cancel-job")
    @Transactional(rollbackFor = Exception.class)
    public void reservedCancel() {
        log.info("已支付预定单取消 - 执行开始");
        LocalDate now = LocalDate.now();
        LocalDateTime nowTime = LocalDateTime.now();
        List<RoomHouseReserve> list = reserveRepository.reservedList(now);
        List<RoomHouse> roomHouseList = new ArrayList<>();
        List<RoomelUpdateReqVO> roomEsVoList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(list)) {
            //查询房源房态记录
            List<Long> roomHouseIds = list.stream().map(s -> s.getRoomHouseId()).collect(Collectors.toList());
            List<RoomStateRecord> roomStateList = roomStateRecordRepository.getListByRoomIdAndIsCurrentState(roomHouseIds);
            Map<Long, List<RoomStateRecord>> roomStateMap = roomStateList.stream().collect(Collectors.groupingBy(RoomStateRecord::getRoomId));
            List<RoomStateRecord> newStateList = new ArrayList<>();
            List<RoomStateRecord> updateStateList = new ArrayList<>();
            list.forEach(s -> {
                s.setStatus(RoomHouseReserveStatus.FAILURE.getCode());
                s.setCancelTime(nowTime);
                s.setCancelReason(reservedCancelReason);
                s.setPaymentStatus(RoomHouseReservePaymentStatus.CANCELED.getCode());
                s.setWithdrawStatus(ReserveWithdrawStatusEnum.PENDING.getValue());
                s.setWithdrawReason(ReserveWithdrawReasonEnum.DEFAULT.getValue().intValue());
                RoomHouse roomHouse = new RoomHouse();
                roomHouse.setId(s.getRoomHouseId());
                roomHouse.setIsLock(Boolean.FALSE);
                roomHouseList.add(roomHouse);
                RoomelUpdateReqVO roomelUpdateReqVO = new RoomelUpdateReqVO();
                roomelUpdateReqVO.setId(s.getRoomHouseId());
                roomelUpdateReqVO.setIsScheduled(Boolean.FALSE);
                roomEsVoList.add(roomelUpdateReqVO);
                //处理房态
                List<RoomStateRecord> roomStateRecords = roomStateMap.get(s.getRoomHouseId());
                //回滚房态到预定的前置房态, 空房 | 入住中
                RoomStateRecord stateRecord = roomStateRecords.stream().filter(record -> RentStatusEnum.SCHEDULED.getValue().equals(record.getState())).findFirst().orElse(null);
                if (stateRecord != null) {
                    stateRecord.setIsCurrentState(false);
                    updateStateList.add(stateRecord);
                }
                Optional<RoomStateRecord> stayingInRecord = roomStateRecords.stream().filter(record -> RentStatusEnum.STAYINGIN.getValue().equals(record.getState())).findFirst();
                if (!stayingInRecord.isPresent()) {
                    RoomStateRecord currentStateRecord = new RoomStateRecord();
                    currentStateRecord.setIsCurrentState(Boolean.TRUE)
                            .setState(RentStatusEnum.EMPTY.getValue())
                            .setRoomId(s.getRoomHouseId())
                            .setBeforeState(RentStatusEnum.SCHEDULED.getValue())
                            .setTenantId(s.getTenantId());
                    newStateList.add(currentStateRecord);
                    roomelUpdateReqVO.setHouseState(RentStatusEnum.EMPTY.getValue());
                } else {
                    roomelUpdateReqVO.setHouseState(RentStatusEnum.STAYINGIN.getValue());
                }
                reserveProducer.paymentCancel(s.getId(), s.getTenantId(), s.getBookerId(), roomHouse.getApartmentId());
            });
            reserveRepository.updateBatchById(list, list.size());
            reserveService.paidTimeOutCancel(list);
            if (CollectionUtil.isNotEmpty(newStateList)) {
                roomStateRecordRepository.saveBatch(newStateList);
            }
            if (CollectionUtil.isNotEmpty(roomHouseList)) {
                roomHouseRepository.updateBatchById(roomHouseList);
            }
            if (CollectionUtil.isNotEmpty(updateStateList)) {
                roomStateRecordRepository.updateBatchById(updateStateList);
            }
            //推动ES状态
            roomEsProducer.updateBatchRoomEl(roomEsVoList);

        }
        log.info("已支付预定单取消 - 执行结束");
    }

    /**
     * 将历史预定预定失效日期,定金支付期限修改为当天的23:59:59
     */
    @XxlJob("room-reserve-init-history-data")
    public void initHistoryData(){
        log.info("将历史预定预定失效日期,定金支付期限修改为当天的23:59:59...开始");
        List<RoomHouseReserve> list = reserveRepository.list();
        list.forEach(roomHouseReserve -> {
            roomHouseReserve.setExpirationDate(LocalDateTimeUtil.endOfDay(roomHouseReserve.getExpirationDate(), true));
            roomHouseReserve.setDepositPaymentTerm(LocalDateTimeUtil.endOfDay(roomHouseReserve.getDepositPaymentTerm(), true));
        });
        reserveRepository.updateBatchById(list);
        log.info("将历史预定预定失效日期,定金支付期限修改为当天的23:59:59...结束");
    }

    /**
     * 将历史预定同步到延迟队列中
     */
    @XxlJob("room-reserve-init-job")
    public void init(){
        log.info("将历史预定同步到延迟队列中...开始");
        //查询所有待支付的预定单
        List<RoomHouseReserve> toBePayList = reserveRepository.toBePay();
        log.info("所有待支付的预定单...{}", JsonUtils.toJsonString(toBePayList));
        toBePayList.forEach(roomHouseReserve -> {
            // 创建延迟任务, 处理待支付取消
            long depositPaymentTermMinutes = LocalDateTimeUtil.between(LocalDateTime.now(), roomHouseReserve.getDepositPaymentTerm()).toMinutes() + 1;
            delayJobService.addDelayJob(DEPOSIT_PAYMENT_TERM, roomHouseReserve.getId(), depositPaymentTermMinutes, TimeUnit.MINUTES);
        });
        //查询所有已预订的预订单
        List<RoomHouseReserve> reservedList = reserveRepository.reserved();
        log.info("所有已预订的预订单...{}", JsonUtils.toJsonString(reservedList));
        reservedList.forEach(roomHouseReserve -> {
            // 创建延迟任务, 处理已预定取消
            long expirationDateMinutes = LocalDateTimeUtil.between(LocalDateTime.now(), roomHouseReserve.getExpirationDate()).toMinutes() + 1;
            delayJobService.addDelayJob(EXPIRATION_DATE, roomHouseReserve.getId(), expirationDateMinutes, TimeUnit.MINUTES);
        });
        log.info("将历史预定同步到延迟队列中...结束");
    }

}
